package com.yuneec.fly.utils;

import android.util.Log;

import com.yuneec.droneservice.protocol.analysis.Analysis;
import com.yuneec.droneservice.utils.ByteUtils;

import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

/**
 * Created by zd.zhang on 2017/9/26.
 */

public class MediaTool {
    public void close() throws IOException {
        if (writer != null) {
            writer.close();
        }
    }

    private static enum NAL {
        NALU_TYPE_SLICE,
        NALU_TYPE_DPA,
        NALU_TYPE_DPB,
        NALU_TYPE_DPC,
        NALU_TYPE_IDR,
        NALU_TYPE_SEI,
        NALU_TYPE_SPS,
        NALU_TYPE_PPS,
        NALU_TYPE_AUD,
        NALU_TYPE_EOSEQ,
        NALU_TYPE_EOSTREAM,
        NALU_TYPE_FILL

    }

    private static final int STATE_NONE = 0;
    private static final int STATE_FIND_HEAD = 1;
    private static final int STATE_FIND_SY = 2;

    private int state = 0;
    private final static byte[] NAL_HEAD = new byte[]{0, 0, 0, 1};
    private int syNum = 0;
    private BufferedWriter writer;

    public MediaTool(String file) throws FileNotFoundException {
        super();
        writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
    }

    public void find(Analysis.Message msg) {
        long t = System.currentTimeMillis();
        int len = msg.length();
        byte[] buf = msg.body();
        int off = msg.offset();
        switch (state) {
            case STATE_NONE:
                findFromFirst(t, buf, off, len);
                break;
            case STATE_FIND_HEAD:
                logType(buf[off], t);
                off++;
                len--;
                state = STATE_NONE;
                findFromFirst(t, buf, off, len);
                break;
            case STATE_FIND_SY:
                for (; syNum > 0; syNum--) {
                    int sl = 4 - syNum;
                    if (isEqualByte(buf, off, NAL_HEAD, syNum, sl)) {
                        off += sl;
                        len -= sl;
                        logType(buf[off], t);
                        off++;
                        len--;
                        state = STATE_NONE;
                        findFromFirst(t, buf, off, len);
                        return;
                    }
                }
                state = STATE_NONE;
                findFromFirst(t, buf, off, len);
                break;
        }
        try {
            writer.flush();
        } catch (IOException e) {
            Log.d("zzd_media", "flush write err:" + e.getMessage());
        }
    }

    private void findFromFirst(long t, byte[] buf, int off, int len) {
        int index;
        while ((index = ByteUtils.simpleFind(buf, off, len, NAL_HEAD)) >= 0) {
            index += 4;
            off += index;
            len -= index;
            if (len > 0) {
                logType(buf[off], t);
            } else {
                state = STATE_FIND_HEAD;
                return;
            }
        }
        if (len > 0 && buf[off + len - 1] == 0) {
            syNum = 1;
            state = STATE_FIND_SY;
        }
        if (len > 1 && buf[off + len - 2] == 0) {
            syNum = 2;
            state = STATE_FIND_SY;
        }
        if (len > 2 && buf[off + len - 3] == 0) {
            syNum = 3;
            state = STATE_FIND_SY;
        }
    }

    private void logType(byte b, long t) {
        int type = b & 31;
        String typeStr = null;
        switch (type) {
            case 1:
                typeStr = "NALU_TYPE_SLICE";
                break;
            case 5:
                typeStr = "NALU_TYPE_IDR";
                break;
            case 7:
                typeStr = "NALU_TYPE_SPS";
                break;
            case 8:
                typeStr = "NALU_TYPE_PPS";
                break;
        }
        if (typeStr != null) {
            try {
                writer.write(typeStr + ":" + t+"\n");
            } catch (IOException e) {
                Log.d("zzd_media", "write err:" + e.getMessage());
            }
        }
    }

    private boolean isEqualByte(byte[] buf, int off, byte[] eq, int eqOff, int eqLen) {
        for (int i = 0; i < eqLen; i++) {
            if (buf[off + i] != eq[eqOff + i]) {
                return false;
            }
        }
        return true;
    }
}
